package hostlink

import (
	"errors"
	"fmt"
	"strconv"
	"strings"

	"gitee.com/acquisition_driver_development/magus_iot/magus/logs"
)

//获取类型
func GetType(tp string) byte {
	tp = strings.ToUpper(tp)
	hktp := byte(0)

	switch tp {
	case "BIT", "COILS":
		hktp = HOSTLINK_COILS
	case "INT16", "INT16_H":
		hktp = HOSTLINK_INT16
	case "INT16_L":
		hktp = HOSTLINK_INT16_L
	case "UINT16", "UINT16_H":
		hktp = HOSTLINK_UINT16
	case "UINT16_L":
		hktp = HOSTLINK_UINT16_L
	case "INT32":
		hktp = HOSTLINK_INT32
	case "INT32_H":
		hktp = HOSTLINK_INT32_H
	case "INT32_L":
		hktp = HOSTLINK_INT32_L
	case "UINT32":
		hktp = HOSTLINK_UINT32
	case "UINT32_H":
		hktp = HOSTLINK_UINT32_H
	case "UINT32_L":
		hktp = HOSTLINK_UINT32_L
	case "FLOAT":
		hktp = HOSTLINK_FLOAT
	case "FLOAT_H":
		hktp = HOSTLINK_FLOAT_H
	case "FLOAT_L":
		hktp = HOSTLINK_FLOAT_L
	case "INT64", "INT64_H":
		hktp = HOSTLINK_INT64_H
	case "INT64_L":
		hktp = HOSTLINK_INT64_L
	case "UINT64", "UINT64_H":
		hktp = HOSTLINK_UINT64_H
	case "UINT64_L":
		hktp = HOSTLINK_UINT64_L
	case "FLOAT64", "DOUBLE":
		hktp = HOSTLINK_DOUBLE
	case "STRING":
		hktp = HOSTLINK_STRING
	default:
		logs.Info("Default Point Type: INT16")
	}
	return hktp
}

/*
*解析fins格式和cmode格式的点地址
*@param
 */

//--------------------------------fins-----------------------------------------
//解析address, fins格式
func ParseFinsAddress2Point(line string) (point *Point, err error) {
	point = new(Point)
	line = strings.ToUpper(line)
	ss := strings.Split(line, ";")
	err = ParseFinsPointInfo(point, ss[0])
	if err != nil {
		return
	}
	switch len(ss) {
	case 1:
		if point.IfBit {
			point.Type = HOSTLINK_COILS
		} else {
			point.Type = HOSTLINK_UINT16
		}
	default:
		point.Type = GetType(ss[1])

	}

	if point.Type == HOSTLINK_COILS { //当把地址写成 XXX;COILS 时判断为 XXX.00
		point.IfBit = true
	}

	return
}

func ParseFinsPointInfo(point *Point, line string) (err error) {
	addr := uint64(0)
	s := GetPrefix(line)
	if s == "" {
		err = errors.New("error address: " + line)
		return
	}
	ss := strings.Split(line, ".")
	l := len(ss)
	if l == 1 {
		point.IfBit = false
		point.Bit = 0
		if strings.HasPrefix(s, "CIO") {
			point.WordCode = WordCode_FINS_Word_CIO
			addr, err = strconv.ParseUint(line[3:], 10, 32) //去掉MemoryType 标志
		} else if strings.HasPrefix(s, "W") {
			point.WordCode = WordCode_FINS_Word_WR
			addr, err = strconv.ParseUint(line[1:], 10, 32)
		} else if strings.HasPrefix(s, "H") {
			point.WordCode = WordCode_FINS_Word_HR
			addr, err = strconv.ParseUint(line[1:], 10, 32)
		} else if strings.HasPrefix(s, "A") {
			point.WordCode = WordCode_FINS_Word_AR
			addr, err = strconv.ParseUint(line[1:], 10, 32)
		} else if strings.HasPrefix(s, "D") {
			point.WordCode = WordCode_FINS_Word_DM
			addr, err = strconv.ParseUint(line[1:], 10, 32)
		} else if strings.HasPrefix(s, "E") {
			es := strings.Split(line, "_")
			if len(es) >= 2 {
				point.WordCode = ParseEWordCode(es[0], false)
				addr, err = strconv.ParseUint(es[1], 10, 32)
			} else { //EN current bank Word 0x98
				point.WordCode = "98"
				addr, err = strconv.ParseUint(line[1:], 10, 32)
			}

		} else {
			err = errors.New("Unsupport Memory Type: " + s)
			return
		}

	} else if l == 2 {
		point.IfBit = true
		bit := int(0)
		bit, err = strconv.Atoi(ss[1])
		if err != nil {
			err = errors.New("address error: " + line)
			return
		}
		point.Bit = byte(bit)
		line = ss[0]
		if strings.HasPrefix(s, "CIO") {
			point.WordCode = WordCode_FINS_Bit_CIO
			addr, err = strconv.ParseUint(line[3:], 10, 32)

		} else if strings.HasPrefix(s, "W") {
			point.WordCode = WordCode_FINS_Bit_WR
			addr, err = strconv.ParseUint(line[1:], 10, 32)

		} else if strings.HasPrefix(s, "H") {
			point.WordCode = WordCode_FINS_Bit_HR
			addr, err = strconv.ParseUint(line[1:], 10, 32)

		} else if strings.HasPrefix(s, "A") {
			point.WordCode = WordCode_FINS_Bit_AR
			addr, err = strconv.ParseUint(line[1:], 10, 32)

		} else if strings.HasPrefix(s, "D") {
			point.WordCode = WordCode_FINS_Bit_DM
			addr, err = strconv.ParseUint(line[1:], 10, 32)

		} else if strings.HasPrefix(s, "E") {
			es := strings.Split(line, "_")
			if len(es) >= 2 {
				point.WordCode = ParseEWordCode(es[0], true)
				addr, err = strconv.ParseUint(es[1], 10, 32)
			} else { // EN current bank word 0A
				point.WordCode = "0A"
				addr, err = strconv.ParseUint(line[1:], 10, 32)
			}

		} else {
			err = errors.New("Unsupport Memory Type: " + s)
			return
		}

	} else {
		err = errors.New("error address: " + line)
	}
	if err != nil {
		return
	}
	point.Address = uint32(addr)
	return
}

//E 功能区单独解析
func ParseEWordCode(s string, ifbit bool) (wordcode string) {
	wordcode = ""
	if len(s) < 2 {
		logs.Error(s, " is not a usefull word code!")
		return
	}
	us, err := strconv.ParseUint(s[1:], 16, 32)
	if err != nil {
		logs.Error(err)
		return
	}
	if ifbit {
		if us <= 0x0F && us >= 0 {
			wordcode = "2" + s[1:2]
		} else if us >= 0x10 && us <= 0x18 {
			wordcode = "E" + s[2:3]
		}

	} else {
		if us <= 0x0F && us >= 0 {
			wordcode = "A" + s[1:2]
		} else if us >= 0x10 && us <= 0x18 {
			wordcode = "6" + s[2:3]
		}
	}

	return
}

//---------------------------------cmode----------------------------------------
//解析address,cmode格式
func ParseCmodeAddress2Point(line string) (point *Point, err error) {
	point = new(Point)
	line = strings.ToUpper(line)
	ss := strings.Split(line, ";")
	err = ParseCmodePointInfo(point, ss[0])
	if err != nil {
		return
	}
	switch len(ss) {
	case 1:
		if point.IfBit {
			point.Type = HOSTLINK_COILS
		} else {
			point.Type = HOSTLINK_UINT16
		}
	default:
		point.Type = GetType(ss[1])
	}
	if point.Type == HOSTLINK_COILS { //当把地址写成 XXX;COILS 时判断为 XXX.00
		point.IfBit = true
	}

	return
}

func ParseCmodePointInfo(point *Point, line string) (err error) {
	addr := uint64(0)
	s := GetPrefix(line)
	if s == "" {
		err = errors.New("error address: " + line)
		return
	}

	ss := strings.Split(line, ".")
	bit := int(0)
	l := len(ss)
	switch l {
	case 1:
		point.IfBit = false
		point.Bit = 0
	default:
		bit, err = strconv.Atoi(ss[1])
		if err != nil || bit > 15 {
			err = errors.New("error address: " + line)
			return
		}
		point.IfBit = true
		point.Bit = byte(bit)
	}

	line = ss[0]
	if strings.HasPrefix(s, "CIO") {
		point.WordCode = WordCode_RR
		addr, err = strconv.ParseUint(line[3:], 10, 32)

	} else if strings.HasPrefix(s, "LR") {
		point.WordCode = WordCode_RL
		addr, err = strconv.ParseUint(line[2:], 10, 32)

	} else if strings.HasPrefix(s, "HR") {
		point.WordCode = WordCode_RH
		addr, err = strconv.ParseUint(line[2:], 10, 32)

	} else if strings.HasPrefix(s, "DM") {
		point.WordCode = WordCode_RD
		addr, err = strconv.ParseUint(line[2:], 10, 32)

	} else if strings.HasPrefix(s, "AR") {
		point.WordCode = WordCode_RA
		addr, err = strconv.ParseUint(line[2:], 10, 32)

	} else if strings.HasPrefix(s, "EM") {
		point.WordCode = WordCode_RE
		addr, err = strconv.ParseUint(line[2:], 10, 32)

	} else if strings.HasPrefix(s, "TC") {
		point.WordCode = WordCode_RC
		addr, err = strconv.ParseUint(line[2:], 10, 32)

	} else if strings.HasPrefix(s, "TG") {
		point.WordCode = WordCode_RG
		addr, err = strconv.ParseUint(line[2:], 10, 32)

	} else {
		err = errors.New("Unsupport Memory Type: " + s)
	}

	if err != nil {
		return
	}
	point.Address = uint32(addr)
	return
}

//--------------------------------implement-------------------------------------
/*
*获取寄存器长度
*@param
*	tp: 点类型
 */
func GetLength(tp byte) (l int) {
	switch tp {
	case HOSTLINK_COILS:
		l = 1
	case HOSTLINK_INT16, HOSTLINK_INT16_L, HOSTLINK_UINT16, HOSTLINK_UINT16_L:
		l = 1
	case HOSTLINK_INT32, HOSTLINK_INT32_H, HOSTLINK_INT32_L, HOSTLINK_UINT32, HOSTLINK_UINT32_H, HOSTLINK_UINT32_L,
		HOSTLINK_FLOAT, HOSTLINK_FLOAT_H, HOSTLINK_FLOAT_L:
		l = 2
	case HOSTLINK_INT64_H, HOSTLINK_INT64_L, HOSTLINK_UINT64_H, HOSTLINK_UINT64_L:
		l = 4
	case HOSTLINK_STRING:
		l = 0
	}
	return
}

/*
*FCS校验
*
 */
func FCS(data []byte) (byte, byte) {
	var f byte
	for _, b := range data[:len(data)] {
		f ^= b
	}
	buf := fmt.Sprintf("%02X", f)
	return buf[0], buf[1]
}

func GetPrefix(s string) string {
	for i := range s {
		if s[i] <= 'Z' && s[i] >= 'A' {
			continue
		}
		return s[:i]
	}
	return ""
}
